// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <lib/trace/observer.h>

#include <zircon/assert.h>

#include <utility>

namespace trace {

TraceObserver::TraceObserver() {}

TraceObserver::~TraceObserver() { Stop(); }

void TraceObserver::Start(async_dispatcher_t* dispatcher, fit::closure callback) {
  ZX_DEBUG_ASSERT(dispatcher);
  ZX_DEBUG_ASSERT(callback);

  Stop();
  callback_ = std::move(callback);

  zx_status_t status = zx::event::create(0u, &event_);
  ZX_ASSERT(status == ZX_OK);
  trace_register_observer(event_.get());

  wait_.set_object(event_.get());
  wait_.set_trigger(ZX_EVENT_SIGNALED);
  BeginWait(dispatcher);
}

void TraceObserver::Stop() {
  wait_.Cancel();
  callback_ = nullptr;

  if (event_) {
    trace_unregister_observer(event_.get());
    event_.reset();
  }
}

void TraceObserver::Handle(async_dispatcher_t* dispatcher, async::WaitBase* wait,
                           zx_status_t status, const zx_packet_signal_t* signal) {
  if (status != ZX_OK) {
    Stop();
    return;
  }

  ZX_DEBUG_ASSERT(status == ZX_OK);
  ZX_DEBUG_ASSERT(signal->observed & ZX_EVENT_SIGNALED);

  // Clear the signal otherwise we'll keep getting called.
  // Clear the signal *before* invoking the callback because there's no
  // synchronization between the engine and the observers, thus it's possible
  // that an observer could get back to back notifications.
  event_.signal(ZX_EVENT_SIGNALED, 0u);

  // Invoke the callback.
  callback_();

  // Tell engine we're done.
  trace_notify_observer_updated(event_.get());

  // Wait again!
  BeginWait(dispatcher);
}

void TraceObserver::BeginWait(async_dispatcher_t* dispatcher) {
  zx_status_t status = wait_.Begin(dispatcher);
  if (status != ZX_OK)
    Stop();
}

}  // namespace trace
